package org.p2psimulator.events;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;

import org.p2psimulator.config.ThreadConfig;

/**
 * Define a estrutura de gera��o e tratamento de eventos. Internamente, possui
 * uma est�ncia est�tica de um executor de tasks. Todas os eventos gerados pelo
 * sistema passam por este executor. Essa decis�o foi tomada para centralizar o
 * mecanismo de distribui��o de mensagens. Na arquitetura anterior cada caixa de
 * mensagens executava seus pr�prios taks de tratamento de mensagens. Embora
 * parecesse mais r�pido, causava um efeito incoveniente devido ao grande n�mero
 * de threads geradas: deixava o sistema imprevis�vel, uma vez que nem todas as
 * trheads eram executadas no momento esperado. Com esta nova abordagem, a
 * gera��o de threads para troca de eventos fica mais organizada e previs�vel. A
 * classe interna EventHandler fornece um mecanismo necess�rio para tratamento
 * instant�neo de eventos.
 *
 * @author Amorim
 *
 */
public abstract class EventEnvironment {

    protected static Map<ExecutorService, Integer> executorsMap = new HashMap<ExecutorService, Integer>();
    protected static int currentHandlersCount = 0;

    public synchronized static void incHandlers() {
        currentHandlersCount++;
    }

    public synchronized static void decHandlers() {
        currentHandlersCount--;
        if (currentHandlersCount <= 0) {
            currentHandlersCount = 0;
        }
    }

    /**
     * Indica se h� EventHandler's ainda rodando nos
     * executores. Independe de o m�todo shutdown()
     * ter sido chamado anteriormente ou n�o.
     * @return
     */
    public synchronized static boolean isRunning() {
        if (currentHandlersCount > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Indica se os executores est�o terminados.
     * Funciona somente se o m�todo shutdown()
     * for chamado anteriormente.
     * @return
     */
    public static boolean isTerminated() {

        boolean ret = true;
        for (ExecutorService exec : executorsMap.keySet()) {
            if (!exec.isTerminated()) {
                ret = false;
            }
        }
        return ret;
    }

    public abstract void handleEvent(Event evt);

    /**
     * Um EventHandler é responsável pelo tratamento imediato de um novo
     * evento gerado dentro do framework de eventos.
     *
     * @author amorim
     *
     */
    class EventHandler implements Runnable {

        Event evt;

        public EventHandler(Event evt) {
            super();
            this.evt = evt;
        }

        public void run() {

            incHandlers();
            handleEvent(evt);
            Thread.yield();
            decHandlers();
        }
    }

    public void newEvent(Event evt) {
        EventHandler alert = new EventHandler(evt);

        for (ExecutorService exec : executorsMap.keySet()) {
            try {
                if (executorsMap.get(exec) < ThreadConfig.MAX_EVENT_THREADS) {
                    synchronized (exec) {
                        if (!exec.isShutdown()) {
                            exec.execute(alert);
                            return;
                        }
                    }
                }
            } catch (RejectedExecutionException e) {
                e.printStackTrace();
            }
        }
    }

    public static void startExecutorServices(int maxExec) {

        executorsMap.clear();
        for (int i = 0; i < maxExec; i++) {
            executorsMap.put(Executors.newFixedThreadPool(ThreadConfig.MAX_EVENT_THREADS), 0);
        }
    }

    public static void shutdownExecutorService() {

        for (ExecutorService exec : executorsMap.keySet()) {
            synchronized (exec) {
                exec.shutdown();
            }
        }
    }
}
